import random
import re

from flask import Blueprint, request, current_app, session
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity, create_refresh_token, get_jwt
from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient

from app import db
from app.models.reader import Reader

tokens = Blueprint('tokens', __name__, url_prefix='/tokens')


@tokens.route('', methods=['POST'])
def login():
    user_id = request.json.get('userId')
    if user_id is None:
        return {'error': 'userId cannot be empty.'}, 400
    password = request.json.get('password')
    if password is None:
        return {'error': 'password cannot be empty.'}, 400

    reader = Reader.query.get(user_id)
    if reader is None:
        return {'error': 'userId does not exist.'}, 401
    elif reader.password != password:
        return {'error': 'password is incorrect.'}, 401

    additional_claims = {
        'role': reader.role
    }

    access_token = create_access_token(identity=user_id, additional_claims=additional_claims)
    refresh_token = create_refresh_token(identity=user_id, additional_claims=additional_claims)

    return {
        'accessToken': access_token,
        'refreshToken': refresh_token,
        'reader': {
            'userId': reader.user_id,
            'username': reader.username,
            'phone': reader.phone,
            'email': reader.email
        }
    }, 200, {'Authorization': f'Bearer {access_token}'}


@tokens.route('/new', methods=['POST'])
def register():
    user_id = request.json.get('userId')
    if user_id is None:
        return {'error': 'userId cannot be empty.'}, 400
    username = request.json.get('username')
    if username is None:
        return {'error': 'username cannot be empty.'}, 400
    password = request.json.get('password')

    phone = request.json.get('phone')
    if phone:
        if not check_phone_format(phone):
            return {'error': 'phone is incorrect.'}, 400
        if Reader.query.filter_by(phone=phone).first():
            return {'error': 'phone has been registered.'}, 400
        code = request.json.get('code')
        if code is None:
            return {'error': 'code cannot be empty.'}, 400
        session_code = session.get('code')
        if code != session_code:
            return {'error': 'code is incorrect.'}, 400

    email = request.json.get('email')
    if email is None:
        return {'error': 'email cannot be empty.'}, 400

    if Reader.query.filter_by(user_id=user_id).first():
        return {'error': 'user id has been registered.'}, 400

    reader = Reader(user_id=user_id, username=username, password=password, phone=phone, email=email)
    db.session.add(reader)
    db.session.commit()

    return "", 201


@tokens.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
    user_id = get_jwt_identity()
    claims = get_jwt()
    additional_claims = {
        'role': claims['role']
    }
    access_token = create_access_token(identity=user_id, additional_claims=additional_claims)
    return {'accessToken': access_token}, 200, {'Authorization': f'Bearer {access_token}'}


@tokens.route('/phone', methods=['POST'])
def login_by_phone():
    phone = request.json.get('phone')
    if phone is None:
        return {'error': 'phone cannot be empty.'}, 400
    elif not check_phone_format(phone):
        return {'error': 'phone is incorrect.'}, 400

    code = request.json.get('code')
    if code is None:
        return {'error': 'code cannot be empty.'}, 400

    session_code = session.get('code')
    if code != session_code:
        current_app.logger.info(f'phone: {phone}, code: {code}, session_code: {session_code}')
        return {'error': 'code is incorrect.'}, 400

    reader = Reader.query.filter_by(phone=phone).first()
    if reader is None:
        return {'error': 'phone does not exist.'}, 401

    additional_claims = {
        'role': reader.role
    }

    access_token = create_access_token(identity=reader.user_id, additional_claims=additional_claims)
    refresh_token = create_refresh_token(identity=reader.user_id, additional_claims=additional_claims)

    return {
        'accessToken': access_token,
        'refreshToken': refresh_token,
        'reader': {
            'userId': reader.user_id,
            'username': reader.username,
            'phone': reader.phone,
            'email': reader.email
        }
    }, 200, {'Authorization': f'Bearer {access_token}'}


#  获取手机验证码
@tokens.route('/phone/code', methods=['GET'])
def get_code():
    phone = request.args.get('phone')
    if phone is None:
        return {'error': 'phone cannot be empty.'}, 400
    elif not check_phone_format(phone):
        return {'error': 'phone format is incorrect.'}, 400

    # 生成一个四位数的数字验证码
    code = random.randint(1000, 9999)

    client = create_client()
    send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
        sign_name='阿里云短信测试',
        template_code='SMS_154950909',
        phone_numbers=phone,
        template_param=f'{{"code":"{code}"}}'
    )
    runtime = util_models.RuntimeOptions()
    try:
        # 复制代码运行请自行打印 API 的返回值
        response = client.send_sms_with_options(send_sms_request, runtime)
        session['code'] = code
        current_app.logger.info(f'成功发送验证码至 {phone} ，验证码为：{code}')
        return '', 200
    except Exception as error:
        # 此处仅做打印展示，请谨慎对待异常处理，在工程项目中切勿直接忽略异常。
        # 错误 message
        current_app.logger.error(error.message)
        # 诊断地址
        current_app.logger.error(error.data.get("Recommend"))
        UtilClient.assert_as_string(error.message)
        return {
            'error': error.message
        }, 400


PHONE_PATTERN = r'^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$'


def check_phone_format(phone):
    return re.match(PHONE_PATTERN, str(phone))


def create_client() -> Dysmsapi20170525Client:
    """
    使用AK&SK初始化账号Client
    @return: Client
    @throws Exception
    """
    # 工程代码泄露可能会导致 AccessKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考。
    # 建议使用更安全的 STS 方式，更多鉴权访问方式请参见：https://help.aliyun.com/document_detail/378659.html。
    config = open_api_models.Config(
        # 必填，请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。,
        access_key_id=current_app.config['ALIBABA_CLOUD_ACCESS_KEY_ID'],
        # 必填，请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。,
        access_key_secret=current_app.config['ALIBABA_CLOUD_ACCESS_KEY_SECRET']
    )
    # Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
    config.endpoint = f'dysmsapi.aliyuncs.com'
    return Dysmsapi20170525Client(config)
